/*
 * Copyright 2010 J Patrick Davenport
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package v4view.web;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import org.apache.commons.collections.CollectionUtils;

/**
 * The parent of any element that wants to have children dom elements.
 * @author J Patrick Davenport
 *
 */
public abstract class ChildbearingPageElement extends PageElement implements IParentElement {
	private List<PageElement>	children;

	public ChildbearingPageElement() {
		super();
	}

	public ChildbearingPageElement(final String _controlId) {
		super(_controlId);
	}

	@Override
	public ChildbearingPageElement addPageElement(final PageElement _element) {
		if (_element == null) { return this; }

		this.initChildren();
		this.children.add(_element);

		// TODO Fix move this to a unique page id.
		// final String childId = this.getElementId() + "|" + _element.getElementId() + this.runningCountOfChildren;
		// _element.setElementId(childId);
		_element.setParent(this);
		final PageElement parent = this.getParent();
		_element.setRequest(parent != null ? parent.getRequest() : this.getRequest());
		_element.setResponse(parent != null ? parent.getResponse() : this.getResponse());

		return this;
	}

	public PageElement findElement(final String _controlId) {
		if (_controlId == null) { return null; }

		if (this.hasChildren()) {
			for (final PageElement e : this.children) {
				if (_controlId.equals(e.getElementId())) { return e; }
			}
		}

		return null;
	}

	public boolean hasChildren() {
		return CollectionUtils.isNotEmpty(this.children);
	}

	@Override
	public void removePageElement(final String _elementId) {
		if (_elementId == null) { return; }

		this.initChildren();
		for (final Iterator<PageElement> iterator = this.children.iterator(); iterator.hasNext();) {
			final PageElement p = iterator.next();
			if (_elementId.equals(p.getElementId())) {
				p.setElementId(null);// Kicks in the natural naming healing process.
				this.children.remove(p);
				break;
			}
		}
	}

	@Override
	public void removePageElement(final PageElement _element) {
		if (_element == null) { return; }
		this.initChildren();
		this.children.remove(_element);
	}

	public int getNumberOfChildren() {
		if (CollectionUtils.isEmpty(this.children)) { return 0; }

		return this.children.size();
	}

	/**
	 * 
	 * @return a nonmodifiable list of chilren elements for this element.
	 */
	public List<PageElement> getChildren() {
		this.initChildren();
		return Collections.unmodifiableList(this.children);
	}

	/**
	 * Provides a default render for elements with children.
	 */
	@Override
	public void render() throws IOException {
		super.render();
		this.renderMe();
	}

	private void renderMe() throws IOException {

		final PrintWriter out = this.getWriter();
		final String renderedAttribute = "".equals(this.renderAttribute()) ? "" : " " + this.renderAttribute().trim();

		final StringBuilder sb = new StringBuilder();
		sb.append("<")
				.append(this.getTag())
				.append(renderedAttribute)
				.append(">");
		final String openTag = sb.toString();
		sb.setLength(0);
		sb.append("</")
				.append(this.getTag())
				.append(">");
		final String closeTag = sb.toString();
		sb.setLength(0);
		if (this.isPrettyPrint()) {
			for (int i = 0; i < this.getTabsIn(); i++) {
				sb.append("\t");
			}
			sb.append(openTag);
			out.println(sb.toString());
			for (final PageElement p : this.children) {
				p.render();
			}
			sb.setLength(0);
			for (int i = 0; i < this.getTabsIn(); i++) {
				sb.append("\t");
			}
			sb.append(closeTag);
			out.println(sb.toString());
		} else {
			out.print(openTag);
			for (final PageElement p : this.children) {
				p.setWriter(out);
				p.render();
			}
			out.print(closeTag);
		}
	}

	public void removeAllChildren() {
		this.initChildren();
		this.children.clear();
	}

	@Override
	public void load() {
		super.load();
		this.loadChildren();
	}

	private void initChildren() {
		if (this.children == null) {
			this.children = new ArrayList<PageElement>();
		}
	}

	private void loadChildren() {
		this.initChildren();
		final int myTabsIn = this.getTabsIn();
		for (final PageElement p : this.children) {
			p.setRequest(this.getRequest());
			p.setResponse(this.getResponse());
			try {
				p.setWriter(this.getWriter());
			} catch (final IOException e) {
				throw new IllegalStateException(e);
			}
			p.setPrettyPrint(this.isPrettyPrint());
			p.setTabsIn(myTabsIn + 1);
			p.load();
		}
	}
}
